package monitor

import (
	"fmt"
	"strconv"
	"sync"
	"time"

	"wowCoupon.com/engine/cacheMachine"
	"wowCoupon.com/models"
	"wowCoupon.com/pkg/e"
	"wowCoupon.com/pkg/tools"
	"wowCoupon.com/service/couponMetaService"
	"wowCoupon.com/service/couponRecordService"
)

// Monitor is an automatic and independent module, it is called by main program
// when the main program executes. It's used for monitoring all CouponMetas' status
// both in Mysql and Redis, and then load or sync data between Mysql and Redis.
// The Mysql is used for persistence as well as the Redis is used for seckill
type Monitor struct {
	CouponMetaService   *couponMetaService.CouponMeta
	CouponRecordService *couponRecordService.CouponRecord
	CacheMachiner       *cacheMachine.CacheMachine
}

func NewMonitor() *Monitor {
	m := &Monitor{
		CouponMetaService:   &couponMetaService.CouponMeta{},
		CouponRecordService: &couponRecordService.CouponRecord{},
		CacheMachiner:       &cacheMachine.CacheMachine{},
	}

	return m
}

// getTargetCouponMetasFromDB gets coupon metas which are around 5 mins to their
// issue start time from mysql
func (m *Monitor) getNotStartCouponMetasFromDB() []*models.CouponMeta {
	fmt.Println("get in getTargetCouponMetasFromDB")
	conditions := []string{
		fmt.Sprintf(
			"status = %s and issue_start_time <= %s",
			fmt.Sprint(e.COUPON_STATUS_NOT_START),
			fmt.Sprint(time.Now().Unix()+300),
		),
	}

	c := couponMetaService.CouponMeta{
		StringConditions: conditions,
	}

	couponMetas, err := c.GetByStringConditions()

	if err != nil {
		return nil
	}

	fmt.Println(couponMetas)
	return couponMetas
}

// loadCouponMetasToRedis loads the coupon metas seckill info to Redis, and also updates
// coupon metas status in Mysql, also loads coupons' validity info to RocketMQ
func (m *Monitor) loadCouponMetasToMiddleWares(couponMetas []*models.CouponMeta) {
	fmt.Println("enter loadCouponMetasToMiddleWares")
	if couponMetas == nil {
		fmt.Println("couponMetas is nil")
		return
	}

	if len(couponMetas) == 0 {
		fmt.Println("couponMetas is []")
		return
	}

	for _, couponMeta := range couponMetas {
		// Change coupon meta's status and update to Mysql, if update failed, just ignore it
		couponMeta.Status = e.COUPON_STATUS_IN_PROGRESS
		if err := m.CouponMetaService.UpdateCouponMetaStatus(couponMeta); err != nil {
			continue
		}

		// Load coupon meta's time and stock info into Redis
		if ok, _ := m.CacheMachiner.PreLoadIssueStartTimeToRedis(couponMeta); !ok {
			continue
		}

		if ok, _ := m.CacheMachiner.PreLoadIssueEndTimeToRedis(couponMeta); !ok {
			continue
		}

		if ok, _ := m.CacheMachiner.PreLoadStockToRedis(couponMeta); !ok {
			continue
		}

		/*
			if ok, err := m.CacheMachiner.LoadRobberListToRedis(couponMeta); !ok {
				panic(err)
				continue
			}*/
	}
}

//syncRedisToMysql sync coupon metas' seckill info which are over issue end time,
//from Redis to Mysql
func (m *Monitor) syncRedisToMysql() error {
	fmt.Println("enter syncRedisToMysql")
	//遍历部门business信息
	businesses, err := m.CacheMachiner.Businesses()

	if err != nil {
		return err
	}

	//遍历每一个部门的每一个优惠券信息
	for _, business := range businesses {
		m.CacheMachiner.ScanBusinessExpirationWith(business, func(hashKvSlice []string) error {
			for i := 0; i < len(hashKvSlice); i += 2 {
				issueEndTime, _ := strconv.ParseInt(hashKvSlice[i+1], 10, 64)
				//judge if the specified seckill avtivity is end
				if time.Now().Unix() >= issueEndTime {
					//fmt.Println("time.Now().Unix() >= issueEndTime")
					//get coupon meta info and stock after seckill from Redis
					businessID := tools.GetBusinessID(business)
					goodID, id := tools.GetGoodIDMetaID(hashKvSlice[i])

					//create couponMeta service instance
					c := couponMetaService.CouponMeta{
						ID:         id,
						BusinessId: businessID,
						GoodId:     goodID,
					}

					//get stock from Redis
					//fmt.Println("从Redis获取stock")
					stock, err := c.GetStockFromCache()
					if err != nil {
						fmt.Println("Error: 从Redis获取stock")
						return err
					}

					//modify
					c.RemainAmount = stock
					c.Status = e.COUPON_STATUS_END

					//sync Redis stock info to Mysql
					//fmt.Println("将Redis库存信息sync到数据库mysql")
					if err = c.UpdateCouponMetaRemainAmount(); err != nil {
						fmt.Println("Error: 将Redis库存信息sync到数据库mysql: ", err)
						return err
					}
				}
			}
			return nil
		})
	}

	return nil
}

func (m *Monitor) Serve() {
	wg := &sync.WaitGroup{}
	wg.Add(2)

	go func() {
		for {
			//加载即将到达活动start时间的优惠券信息，mysql -> redis
			couponMetas := m.getNotStartCouponMetasFromDB()
			m.loadCouponMetasToMiddleWares(couponMetas)
			time.Sleep(10 * time.Second)
		}
	}()

	go func() {
		for {
			//将活动时间已经end的优惠券信息sync回数据库，redis -> mysql
			m.syncRedisToMysql()
			time.Sleep(5 * time.Second)
		}
	}()

	wg.Wait()
}
